/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 *
 * Contains: framebuffer driver
 *
 * Change Logs:
 * Date           Author            Notes
 * 2023-2-4       planck             Init
 */
#define NX_LOG_NAME "framebuffer"
#define DRV_NAME "lcd framebuffer driver"
#define DRV_VERSION "0.1"

#define DEV_NAME "fb0"

#include <base/log.h>
#include <base/page.h>
#include <base/driver.h>
#include <base/malloc.h>
#include <base/debug.h>
#include <base/memory.h>
#include <base/ioremap.h>
#include <drvfw/framebuffer.h>

#include <drivers/gpio.h>
#include <drivers/lcd.h>

#include <regs.h>
#include <base/cache.h>

extern NX_U8 lcd_fb[LCD_WIDTH * LCD_HEIGHT * 4];
NX_PRIVATE NX_FramebufferInfo fb0Info;



NX_PRIVATE NX_Error LCD_Control(struct NX_Device *device, NX_U32 cmd, void *arg)
{
    NX_FramebufferInfo *info;
    NX_FramebufferInfo *fbInfo;

    fbInfo = device->extension;
    NX_ASSERT(fbInfo);
    switch (cmd)
    {
    case NX_FRAMEBUFFER_CMD_GETINFO:
        info = (NX_FramebufferInfo *)arg;
        if (info)
        {
            info->bitsPerPixel = fbInfo->bitsPerPixel;
            info->bytesPerScanLine = fbInfo->bytesPerScanLine;
            info->xResolution = fbInfo->xResolution;
            info->yResolution = fbInfo->yResolution;
            info->phyBasePtr = fbInfo->phyBasePtr;
            NX_Printf("info -> phyBasePtr = %x\n", info->phyBasePtr);
            NX_Printf("info -> xResolution = %d\n", info->xResolution);
            NX_Printf("info -> yResolution = %d\n", info->yResolution);
            NX_Printf("info -> bitsPerPixel = %d\n", info->bitsPerPixel);
        }

        break;
    case NX_FRAMEBUFFER_CMD_PRESENT:
        NX_DcacheFlushRange(lcd_fb, LCD_WIDTH * LCD_HEIGHT * 4);
        
        break;
    default:
        return NX_EINVAL;
    }

    return NX_EOK;
}

NX_PRIVATE NX_Error LCD_Mappable(struct NX_Device *device, NX_Size length, NX_U32 prot, NX_Addr *outPhyAddr)
{
    NX_FramebufferInfo *fbInfo;

    fbInfo = device->extension;
    NX_ASSERT(fbInfo);

    if (!fbInfo->phyBasePtr)
    {
        return NX_ENORES;
    }

    if (length > fbInfo->bytesPerScanLine * fbInfo->yResolution)
    {
        return NX_EPERM;
    }

    *outPhyAddr = fbInfo->phyBasePtr;

    return NX_EOK;
}

NX_PRIVATE NX_DriverOps FB_DriverOps = {
    .control = LCD_Control,
    .mappable = LCD_Mappable,
};



NX_PRIVATE void LCD_DriverInit(void)
{

    NX_Device *device;

    fb0Info.bitsPerPixel = 32;
    fb0Info.xResolution = 800;
    fb0Info.yResolution = 480;
    fb0Info.bytesPerScanLine = fb0Info.xResolution * fb0Info.bitsPerPixel / 8;
    fb0Info.phyBasePtr = lcd_fb;

    NX_Driver *driver = NX_DriverCreate(DRV_NAME, NX_DEVICE_TYPE_SCREEN, 0, &FB_DriverOps);
    if (driver == NX_NULL)
    {
        NX_LOG_E("create drive failed!");
        return;
    }

    if (NX_DriverAttachDevice(driver, DEV_NAME, &device) != NX_EOK)
    {
        NX_LOG_E("attach device %s failed!", DEV_NAME);
        NX_DriverDestroy(driver);
        return;
    }

    device->extension = &fb0Info;

    if (NX_DriverRegister(driver) != NX_EOK)
    {
        NX_LOG_E("register driver %s failed!", DRV_NAME);
        NX_DriverDetachDevice(driver, DEV_NAME);
        NX_DriverDestroy(driver);
        return;
    }
    NX_LOG_I("register driver %s succeeded!", DRV_NAME);
    // lcd_show_logo();
}

NX_PRIVATE void LCD_DriverExit(void)
{
    NX_DriverCleanup(DRV_NAME);
}

NX_DRV_INIT(LCD_DriverInit);
NX_DRV_EXIT(LCD_DriverExit);

